/*
 * @Author: sunqimeng 1435585893@qq.com
 * @Date: 2023-06-02 20:05:09
 * @LastEditors: sunqimeng 1435585893@qq.com
 * @LastEditTime: 2023-06-03 23:29:12
 * @FilePath: \diff\main.js
 * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
 */

/**
 * 虚拟节点
 * @date 2023/6/2 - 20:36:01
 *
 * @param {*} sel
 * @param {*} data
 * @param {*} children
 * @param {*} text
 * @param {*} elm
 * @returns {{ sel: any; data: any; children: any; text: any; elm: any; key: any; }}
 */
function vnode(sel, data, children, text, elm) {
    const key = data === undefined ? undefined : data.key;
    return { sel, data, children, text, elm, key }
}

/**
 * 生成虚拟节点
 * @date 2023/6/2 - 21:00:22
 *
 * @param {*} sel 标签
 * @param {*} data 属性
 * @param {*} child 子项
 */
function h(sel, data, child) {
    if (typeof child === 'string' || typeof child === 'number') {
        // e.innerHTML = child
        return vnode(sel, data, null, child)
    } else {
        child = child || []
        if (!Array.isArray(child)) {
            child = [child]
        }
        // TODO 不处理child为string和vnode混合类型，数组时只能是vnode
        return vnode(sel, data, child)
    }
}


/**
 * 根据虚拟节点创建dom
 * @date 2023/6/2 - 21:16:44
 *
 * @param {*} vnode
 */
function creatEle(vnode) {
    // console.log('vnode创建dom')
    const item = document.createElement(vnode.sel)
    // 为节点设置属性
    // TODO 仅考虑节点更新，不考虑属性更新
    for (let key in vnode.data) {
        item.setAttribute(key, vnode.data[key]);
    }
    vnode.elm = item
    if (vnode.text) {
        item.innerHTML = vnode.text
    } else if (vnode.children) {
        for (let index = 0; index < vnode.children.length; index++) {
            const element = vnode.children[index];
            vnode.elm.appendChild(creatEle(element).elm)
        }
    }
    return vnode
}



/**
 * 比较替换
 * @date 2023/6/3 - 13:35:28
 *
 * @param {*} newVNode 新虚拟节点
 * @param {*} oldVNode 老虚拟节点（仅支持已上树）
 */
function patch(newVNode, oldVNode) {
    // key值相同精细化比较，key值不同暴力替换
    if (newVNode.key === oldVNode.key) {
        // 是同一个节点，执行精细化比较
        newVNode.elm = oldVNode.elm // TODO 为新节点elm的引用地址指向已有的dom，方便调试维护，其实应该支持dom转vnode
        
        // oldVNode和newVNode是内存上的同一对象，即完全相同，不做任何处理
        if (newVNode === oldVNode) {
            console.log('同一对象, 不做任何处理')
            return oldVNode
        }

        // 新节点有文字，对比更新文字即可
        if (newVNode.text) {
            if (newVNode.text !== oldVNode.text) {
                console.log('全部更新为文字')
                // dom更新
                oldVNode.elm.innerHTML = newVNode.text
                return newVNode
            }
            console.log('文字比较全部相同')
            return newVNode
        }

        // 不是文字更新就是子项更新

        // 老节点无子项，子项更新
        if (!oldVNode.children || oldVNode.children.length === 0) {
            oldVNode.elm.innerText = '' // 清空父节点
            console.log('全部更新为子项')
            for (let index = 0; index < newVNode.children.length; index++) {
                const element = newVNode.children[index];
                oldVNode.elm.appendChild(creatEle(element).elm)
            }
            return newVNode
        }

        // 老节点有子项，精细化子项对比
        console.log('精细化子项对比')
        const nc = newVNode.children
        const oc = oldVNode.children
        let newStart = 0 // 新前
        let newEnd = nc.length - 1 // 新后
        let oldStart = 0 // 旧前
        let oldEnd = oc.length - 1 // 旧后
        for (; newStart <= newEnd && oldStart <= oldEnd; () => {
            
        }) {
            if(nc[newStart] &&  oc[oldStart] && nc[newStart].key ===  oc[oldStart].key) {
                // 新前旧前命中
                console.log('新前旧前命中')
                patch(nc[newStart], oc[oldStart])
                newStart++
                oldStart++
                continue
            }
            if(nc[newEnd] &&  oc[oldEnd] && nc[newEnd].key ===  oc[oldEnd].key) {
                // 新后旧后命中
                console.log('新后旧后命中')
                patch(nc[newEnd], oc[oldEnd])
                newEnd--
                oldEnd--
                continue
            }
            if(nc[newEnd] &&  oc[oldStart] && nc[newEnd].key ===  oc[oldStart].key) {
                // 新后旧前命中
                console.log('新后旧前命中')
                patch(nc[newEnd], oc[oldStart])
                console.log(oc[oldStart].elm, oc[oldEnd].elm.nextSibling)
                oldVNode.elm.insertBefore(oc[oldStart].elm, oc[oldEnd].elm.nextSibling) // 旧前 移到 旧后 后
                oc[oldStart].elm = undefined // 旧前 原位置undefined防止错误删除与索引改变，注意循环时排除undefined
                newEnd--
                oldStart++
                continue
            }
            if(nc[newStart] &&  oc[oldEnd] && nc[newStart].key ===  oc[oldEnd].key) {
                // 新前旧后命中
                console.log('新前旧后命中')
                patch(nc[newStart], oc[oldEnd])
                oldVNode.elm.insertBefore(oc[oldEnd].elm, oc[oldStart].elm) // 旧后移到 旧前 前
                oc[oldEnd].elm = undefined // 旧后 原位置undefined防止错误删除与索引改变，注意循环时排除undefined
                newStart++
                oldEnd--
                continue
            }
            // 全部未命中，在旧节点中找当前新前相同的key值节点
            console.log('四种情况均未命中')
            let findIndex = -1
            // todo map更好
            for (let index = oldStart; index < oc.length; index++) {
                if (!oc[index]) {
                    continue
                }
                const key = oc[index].key;
                if (key === nc[newStart].key) {
                    findIndex = index
                    break
                }
            }
            if (findIndex === -1) {
                // 未找到
                console.log('未找到')
                let newDomObj = creatEle(nc[newStart]).elm
                oldDomObj = oc[oldStart].elm
                oldVNode.elm.insertBefore(newDomObj, oldDomObj)
            } else {
                // 找到了
                console.log('找到了')
                const findVNode = oc[findIndex]
                // 继续精细化比较
                patch(nc[newStart], findVNode)
                oldVNode.elm.insertBefore(findVNode.elm, oc[oldStart].elm)
                oc[findIndex] = undefined // 原位置undefined防止错误删除与索引改变，注意循环时排除undefined
            }
            newStart++
        }
        if (newStart > newEnd) {
            // 新前大于新后，说明老的长，老节点中间是删除逻辑
            console.log('新前大于新后，删除逻辑')
            for (let index = oldStart; index <= oldEnd; index++) {
                if (oc[index] === undefined) {
                    continue;
                }
                let oldDomObj = oc[index].elm
                oldVNode.elm.removeChild(oldDomObj)
            }
            return newVNode
        }
        if (oldStart > oldEnd) {
            // 老前大于老后，说明新的长，老节点中间是新增逻辑
            console.log('老前大于老后，新增逻辑')
            for (let index = newStart; index <= newEnd; index++) {
                // if (nc[index] === undefined) {
                //     continue;
                // }
                let newDomObj = creatEle(nc[index]).elm
                let oldDomObj = null
                // 旧前节点存在直接在它之前插入
                if (oc[oldStart]) {
                    oldDomObj = oc[oldStart].elm
                    console.log('oldVNode.elm', oldVNode.elm)
                    oldVNode.elm.insertBefore(newDomObj, oldDomObj)
                }
                // 旧后节点的后面依次追加
                if (oc[oldEnd]) {
                    oldDomObj = oc[oldEnd].elm
                    if(oldVNode.elm.lastChild === oldDomObj){
                        oldVNode.elm.appendChild(newDomObj)
                    }else{
                        oldVNode.elm.insertBefore(newDomObj, oldDomObj.nextSibling)
                    }
                }
                console.warn('旧前旧后不可能同时没有')
            }
            return newVNode
        }
        console.warn('按照逻辑应该return出去')
    } else {
        // 不是一个节点，新的替换旧的
        console.log('不是一个节点，新的替换旧的')
        let newDomObj = creatEle(newVNode).elm
        let oldDomObj = oldVNode.elm
        oldDomObj.parentNode.insertBefore(newDomObj, oldDomObj)
        oldDomObj.parentNode.removeChild(oldDomObj)
        return newVNode
    }
}

export default {
    vnode,
    h,
    creatEle,
    patch
}